#include <fstream>
#include <pthread.h>
#include <vector>
#include <sstream>
#include <unordered_map>
#include "Socket.hpp"

const uint16_t port = 8000;

const string wroot = "./wroot";
const string seq = "\r\n";
const string homepage = "index.html";

class server;
class ThreadData
{
public:
    int _sockfd;
    server *_this;
};

class Request
{
public:
    void deserialize(string message)
    {
        while (true)
        {
            size_t pos = message.find(seq);
            if (pos == string::npos)
            {
                break;
            }

            string temp = message.substr(0, pos);
            if (temp.empty())
            {
                break;
            }
            _reqhead.push_back(temp);
            // pos + seq
            message.erase(0, pos + seq.size());
        }

        _text = message;
    }

    void parse()
    {
        stringstream s(_reqhead[0]);
        s >> _method >> _url >> _httpversion;
        _filepath = wroot;
        // wroot/index.html
        if (_url == "/" || _url == "/index.html")
        {
            _filepath += "/";
            _filepath += homepage;
        }
        else
        {
            // 用户/a/b  文件./wroot/a/b
            _filepath += _url;
        }

        auto pos = _filepath.rfind(".");
        if (pos == string::npos)
        {
            _suffix = ".html";
        }
        else
        {
            _suffix = _filepath.substr(pos);
        }

    }

    void debugprint()
    {
        for (auto &line : _reqhead)
        {
            cout << "---------------------------" << endl;
            cout << line << "\n";
        }

        cout << "method:" << _method << endl;
        cout << "url:" << _url << endl;
        cout << "http-version:" << _httpversion << endl;
        cout << "file_path:" << _filepath << endl;
        cout << "content-type: " << _suffix << endl;
        cout << _text << endl;
    }

public:
    vector<string> _reqhead;
    string _text;

    string _method;
    string _url;
    string _httpversion;
    string _filepath;

    string _suffix;
};

class server
{
public:
    void ContentTable()
    {
        _content_type.insert({".html", "text/html"});
        _content_type.insert({".jpg", "image/jpeg"});
        _content_type.insert({".png", "image/png"});

    }

    void start()
    {
        ContentTable();
        _listensock.Socket();
        _listensock.Bind(port);
        _listensock.Listen();

        cout << "server init done" << endl;
        for (;;)
        {
            string ip;
            uint16_t port;
            int sockfd = _listensock.Accept(&ip, &port);
            if (sockfd > 0)
            {
                cout << "get a new link:" << ip << "," << port << endl;
                pthread_t tid;
                ThreadData *data = new ThreadData;
                data->_sockfd = sockfd;
                data->_this = this;
                pthread_create(&tid, nullptr, routine, data);
            }
        }
    }

    static void *routine(void *args)
    {
        pthread_detach(pthread_self());
        ThreadData *dat = static_cast<ThreadData *>(args);
        dat->_this->HandlerHttp(dat->_sockfd);
        close(dat->_sockfd);
        delete dat;
        return nullptr;
    }

    string SuffixDesc(const string& suffix)
    {
        auto n = _content_type.find(suffix);
        if (n == _content_type.end())
        {
            return ".html";
        }
        else
        {
            return _content_type[suffix];
        }
    }

    void HandlerHttp(int sockfd)
    {
        char buff[1024];
        ssize_t n = recv(sockfd, buff, sizeof(buff) - 1, 0);
        if (n > 0)
        {
            buff[n] = 0;
            // 假设读到了完整报文
            // 分割请求，获取请求文件
            
            Request req;
            req.deserialize(buff);
            req.parse();
            req.debugprint();
            // 响应内容
          
            string text = ReadHtmlContent(req._filepath); //失败
            bool ok = true;
            if (text.empty())
            {
                ok = false;
                string err_html = wroot;
                err_html += "/err.html";
                text = ReadHtmlContent(err_html);
            }

            string response_line;
            if (ok)
            {
                response_line = "HTTP/1.0 200 OK\r\n";
            }
            else
            {
                response_line = "HTTP/1.0 404 Not Found\r\n";
            }     

            //response_line = "HTTP/1.0 302 Found\r\n";
            string response_head = "Content-Length: ";
            response_head += to_string(text.size());
            response_head += "\r\n";
            //response_head += "location: https://home.firefoxchina.cn/\r\n";
            response_head += "Content-Type: ";
            response_head += SuffixDesc(req._suffix);
            //cout << "Content-Type: " << SuffixDesc(req._suffix) << endl;
            response_head += "\r\n";
            response_head += "Set-Cookie: name=www&&pass=123\r\n";
            string block_line = "\r\n";

            string response = response_line;
            response += response_head;
            response += block_line;
            response += text;

            //cout << response << endl;
            send(sockfd, response.c_str(), response.size(), 0);
        }
    }

    // 读取html
    static string ReadHtmlContent(const string htmlpath)
    {
        // 图片数据需要二进制读取
        ifstream in(htmlpath, ios::binary);
        in.seekg(0, ios_base::end);
        std::streampos len = in.tellg();
        in.seekg(0, ios_base::beg);

        string content;
        content.resize(len);
        in.read((char *)content.c_str(), content.size());
        // if (!in.is_open())
        // {

        //     return "";
        // }

        // string line;
        // string content;
        // while (getline(in, line))
        // {
        //     content += line;
        // }

        in.close();
        return content;
    }

private:
    Sock _listensock;
    unordered_map<string, string> _content_type;
};